#include <X11/Xlib.h>
#include <X11/extensions/XInput.h>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

enum
{
	INPUTEVENT_MOTION_NOTIFY,

	INPUTEVENT_MAX
};

int gnInputEvent[INPUTEVENT_MAX] = { 0 };
int nEventListCnt = 0;
XDeviceInfoPtr devList = NULL;
XDevice* dev = NULL;
int DevListCnt = 0;
XEventClass eventList[32];
XEventClass cls;


char *dev_name = "Wacom Pen";
int timeout = 2000000;

Window create_simple_window(Display* display, int width, int height, int x, int y)
{
    int screen_num = DefaultScreen(display);
    int win_border_width = 2;
    Window win;

    win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
                            x, y, width, height, win_border_width,
                            BlackPixel(display, screen_num),
                            WhitePixel(display, screen_num));

    XMapWindow(display, win);
    XFlush(display);
    return win;
}

Display* InitXInput(char* name)
{
	Display* pDisp;
	int nMajor, nFEV, nFER;

	pDisp = XOpenDisplay(name);
	if (!pDisp)
	{
		fprintf(stderr,"Failed to connect to X server.\n");
		return NULL;
	}

	//XSetErrorHandler(ErrorHandler);

	XSynchronize(pDisp,1 /*sync on*/);
	
	if (!XQueryExtension(pDisp,INAME,&nMajor,&nFEV,&nFER))
	{
		fprintf(stderr,"Server does not support XInput extension.\n");
		XCloseDisplay(pDisp);
		return NULL;
	}

	return pDisp;
}

int done = 0;
int started = 0;

static void sig_alrm(int signo) {
    signal(SIGALRM, sig_alrm);
    done = 1;
}

// Look for pen tablet
void setup_tablet(Display* display, Window win) {
    int i;
    devList = (XDeviceInfoPtr) XListInputDevices(display, &DevListCnt);
    XDeviceInfoPtr devInfo = NULL;
    for (i=0; i<DevListCnt; ++i)
    {
        if ( (((devList[i].name[0] == 'P') && (devList[i].name[1] == 'T')) || // Pen Tablet
             (!strcasecmp(devList[i].name,dev_name)))// Wacom 
             && devList[i].num_classes ) { 
        		devInfo = devList + i;
        		printf("Device Name: %s\n", devList[i].name);
        	}
    }
    if (!devInfo)
	{
		fprintf(stderr,"No pen tablet input device found.\n");
		exit(1);
	}
    dev = XOpenDevice(display,devInfo->id);
    XFreeDeviceList(devList); devList = NULL;
	if (!dev)
	{
		fprintf(stderr,"Unable to open input device \n");
		exit(1);
	}

	/* motion events */
	DeviceMotionNotify(dev,gnInputEvent[INPUTEVENT_MOTION_NOTIFY],cls);
	if (cls) eventList[nEventListCnt++] = cls;
  
    XGrabDevice(display, dev, win, 0, nEventListCnt, eventList,
		                    GrabModeAsync, GrabModeAsync, CurrentTime);
}

int main(int argc, char* argv[])
{
    int mouseevcount = 0;
    int tabletevcount = 0;
    Display* display;
    int screen_num;
    Window win;
    unsigned int display_width, display_height;
    unsigned int width, height;
    char *display_name = getenv("DISPLAY");
    
    if (argc > 1) {
        dev_name = argv[1];
        if (argc > 2) {
            timeout = atoi(argv[2]);
        }        
    } else {
        printf("USAGE: %s [device_name [timeout]]\n\n", argv[0]);
    }
    
    signal(SIGALRM, sig_alrm);

    /* open connection with the X server. */
    display = XOpenDisplay(display_name);
    if (display == NULL) {
    fprintf(stderr, "%s: cannot connect to X server '%s'\n",
            argv[0], display_name);
    exit(1);
    }

    /* get the geometry of the default screen for our display. */
    screen_num = DefaultScreen(display);
    display_width = DisplayWidth(display, screen_num);
    display_height = DisplayHeight(display, screen_num);

    width = (display_width / 2);
    height = (display_height / 2);
    win = create_simple_window(display, width, height, 0, 0);

    /* subscribe to the given set of event types. */
    if (0)
        XSelectInput(display, win, KeyPressMask | ButtonPressMask | Button1MotionMask );
    else
        XSelectInput(display, win, KeyPressMask | PointerMotionMask );
  
    setup_tablet(display, win);  


    printf("Start using mouse or pen in window, timeout is %d µs\n", timeout);
    /* perform an events loop */
    {
        XEvent an_event;
        while (!done) {
            XNextEvent(display, &an_event);
            switch (an_event.type) {

            case MotionNotify:
                ++mouseevcount;
                if (!started) {
                    ualarm(timeout,0);
                    started = 1;
                }
            	break;

            case DestroyNotify:
            case KeyPress:
                printf("Exiting...\n");
                done = 1;
                break;

            default: 
                if (an_event.type == gnInputEvent[INPUTEVENT_MOTION_NOTIFY]) {
                    ++tabletevcount;
                    if (!started) {
                        ualarm(timeout,0);
                        started = 1;
                    }
                } else {
                    /* ignore any other event types. */
                    printf("other event: %d\n", an_event.type);
                }
                break;
            }
        }
    }

    if (dev)
        XCloseDevice(display, dev);
    XCloseDisplay(display);
    printf("Mouse events: %d, Tablet events: %d\n", mouseevcount, tabletevcount);
    return 0;
}
